﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace Runtime.Services.Battle {

    /// <summary>
    /// 战场路径
    /// </summary>
    public interface IDFR_BattleWay : IEnumerable<IDFR_BattleWay.Point>, IEnumerable {

        /// <summary>
        /// 路点
        /// </summary>
        public readonly struct Point {

            /// <summary>
            /// 单元格坐标
            /// </summary>
            public DFR_BattleCellAxis Axis { get; init; }

            /// <summary>
            /// 下一个转角距离
            /// </summary>
            public int NextTurnDistance { get; init; }

            /// <summary>
            /// 是否是转角
            /// </summary>
            public bool IsTurn { get; init; }

            /// <summary>
            /// 上一个转角距离
            /// </summary>
            public int PreviousTurnDistance { get; init; }

            public override bool Equals(object obj) {
                if (obj is Point other) {
                    return other.Axis == Axis;
                }
                return false;
            }

            public override int GetHashCode() {
                return Axis.GetHashCode();
            }

            public static bool operator ==(Point left, Point right) => left.Equals(right);
            public static bool operator !=(Point left, Point right) => !left.Equals(right);
        }

        /// <summary>
        /// ID
        /// </summary>
        DFR_BattleWayID ID { get; }

        /// <summary>
        /// 总长度
        /// </summary>
        F64 Length { get; }

        [Obsolete("", true)]
        /// <summary>
        /// 按<paramref name="index"/>索引取得路点
        /// </summary>
        /// <param name="index">索引</param>
        Point this[int index] { get; }

        Point Get(int index, bool isReverse);

        /// <summary>
        /// 路点数量
        /// </summary>
        int Count { get; }

        /// <summary>
        /// 起始路点
        /// </summary>
        Point GetStartPoint(bool isReverse);

        /// <summary>
        /// 终点路点
        /// </summary>
        Point GetEndPoint(bool isReverse);

        /// <summary>
        /// 取得下一坐标
        /// </summary>
        /// <param name="wayPosition">路径位置</param>
        DFR_BattleCellAxis GetNextAxis(F64 wayPosition, bool isReverse);
    }

    public class DFR_BattleWay : IDFR_BattleWay, IDisposable, IReadOnlyList<IDFR_BattleWay.Point> {

        private class TempPoint {

            public DFR_BattleCellAxis Axis;

            public int NextTurnDistance;

            public bool IsTurn;

            public int PreviousTurnDistance;
        }

        public DFR_BattleWayID ID { get; init; }

        private IReadOnlyList<IDFR_BattleWay.Point> m_List;

        /// <summary>
        /// 转弯点列表
        /// </summary>
        private List<IDFR_BattleWay.Point> m_TurnList = new();

        /// <summary>
        /// 路径全长
        /// </summary>
        public F64 Length { get; private set; }

        public int Count => m_List.Count;

        [Obsolete("", true)]
        public IDFR_BattleWay.Point this[int index] => throw new NotImplementedException();

        [Obsolete("", true)]
        public IDFR_BattleWay.Point GetStartPoint(bool isReverse) => Get(m_List.Count - 1, isReverse);

        [Obsolete("", true)]
        public IDFR_BattleWay.Point GetEndPoint(bool isReverse) => Get(0, isReverse);

        public IDFR_BattleWay.Point Get(int index, bool isReverse) => isReverse ? m_List[m_List.Count - (index + 1)] : m_List[index];

        public DFR_BattleWay(IReadOnlyList<DFR_BattleCellAxis> list) {
            var pointList = new List<TempPoint>();
            var currentPointList = new List<TempPoint>();
            var forwardOffset = Vector2Int.zero;
            for (int i = 0; i < list.Count; i++) {
                var axis = list[i];
                foreach (var point in currentPointList) {
                    point.PreviousTurnDistance++;
                }
                currentPointList.Add(new TempPoint { Axis = axis, NextTurnDistance = currentPointList.Count });
                if (i == 0) {
                    var currentPoint = currentPointList[^1];
                    currentPoint.IsTurn = true;
                } else if (i == list.Count - 1) {
                    var currentPoint = currentPointList[^1];
                    currentPoint.PreviousTurnDistance = 0;
                    currentPoint.IsTurn = true;
                    pointList.AddRange(currentPointList);
                } else if (currentPointList.Count > 1) {
                    var lastAxis = list[i - 1];
                    var currentForwardOffset = (Vector2Int)axis - (Vector2Int)lastAxis;
                    if (forwardOffset != Vector2Int.zero) {
                        if (forwardOffset != currentForwardOffset) {
                            var currentPoint = currentPointList[^1];
                            currentPoint.NextTurnDistance = 1;
                            var lastPoint = currentPointList[^2];
                            lastPoint.IsTurn = true;
                            pointList.AddRange(currentPointList);
                            currentPointList.Clear();
                            currentPointList.Add(lastPoint);
                            currentPointList.Add(currentPoint);
                        }
                    }
                    forwardOffset = currentForwardOffset;
                }
            }
            foreach (var point in currentPointList) {
                point.PreviousTurnDistance++;
            }
            m_List = pointList.Distinct().Select(p => {
                var point = new IDFR_BattleWay.Point { Axis = p.Axis, PreviousTurnDistance = p.PreviousTurnDistance - 1, NextTurnDistance = p.NextTurnDistance, IsTurn = p.IsTurn };
                if (point.IsTurn) {
                    m_TurnList.Add(point);
                }
                return point;
            }).ToList();
            //foreach (var item in m_List) {
            //    UnityEngine.Debug.Log($"{item.Axis}, Next={item.NextTurnDistance}, Privious={item.PreviousTurnDistance}, {nameof(item.IsTurn)}={item.IsTurn}");
            //}
            Length = (F64)(list.Count - 0.5f);
        }

        void IDisposable.Dispose() {
            m_List = null;
            m_TurnList = null;
        }

        public IEnumerator<IDFR_BattleWay.Point> GetEnumerator() => m_List.GetEnumerator();

        IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)m_List).GetEnumerator();

        public DFR_BattleCellAxis GetNextAxis(F64 wayPosition, bool isReverse) => BattleServiceTD.Instance.GetWayNextAxis(this, wayPosition, isReverse);
    }
}
